package triTree;

import transaction.Transaction;
import triInstance.*;
import triFileSystem.*;
import triBasic.ISet;
import triDataStruct.*;
import triStorage.*;

// must gurantee there is only one?
public class SetManager extends MemoryBuffer implements ISetManager {

	private TriMMPFile mmpfile;
	private TriMMPFile inodeFile;
	
	public SetManager(int set_buffer_size, String set_filename, int set_pagebuffer_size, 
			int set_pagesize, String set_inode_filename, int set_inode_pagebuffer_size,
			int set_inode_pagesize) {
		super(set_buffer_size);
		
		mmpfile = new TriMMPFile(set_filename, set_pagebuffer_size, set_pagesize);
		if (set_filename.equals(set_inode_filename))
			inodeFile = mmpfile;
		else
			inodeFile = new TriMMPFile(set_inode_filename, set_inode_pagebuffer_size, set_inode_pagesize);
	}
	
	@Override
	public void init(Transaction tx) {
		mmpfile.open();
		inodeFile.open();
	}
	
	@Override
	public int createNewSet() {
		//int inodePageId = mmpfile.allocNewPage();
		int inodePageId = inodeFile.allocNewPage();
		return inodePageId;
	}
	
	@Override
	public void insertItem(NodeSnapshot snapshot, String item, Transaction tx) {
		int inodePageId = snapshot.getSetInodePageId();
		int index = getTriSet(inodePageId, tx);
		InsertableTriSet iset = (InsertableTriSet)buffer[index];
		iset.insertItem(item, tx);
		unPin(index);
	}
	
	@Override
	public void removeItem(NodeSnapshot snapshot, String item, Transaction tx) {
		int inodePageId = snapshot.getSetInodePageId();
		int index = getTriSet(inodePageId, tx);
		InsertableTriSet iset = (InsertableTriSet)buffer[index];
		PageDir inode = iset.getInodeSnapshot(tx);
		long pos = iset.getFilePos(tx);
		
		unPin(index);
		IReader reader = new EndRPageArray(inode, pos, mmpfile);
		IRandIntAccess access = new RandPageAccess(mmpfile);
		RemovableTriSet rset = new RemovableTriSet(reader, access); 
		rset.removeItem(item, tx);
		
	}
	
	@Override
	public ISet<String> queryTriSet(NodeSnapshot snapshot, Transaction tx) {
		int inodePageId = snapshot.getSetInodePageId();
		int index = getTriSet(inodePageId, tx);
		InsertableTriSet iset = (InsertableTriSet)buffer[index];
		PageDir inode = iset.getInodeSnapshot(tx);
		long pos = iset.getFilePos(tx);
		unPin(index);
		
		IReader reader = new EndRPageArray(inode, pos, mmpfile);
		ReadableTriSet qset = new ReadableTriSet(reader);
		return qset;
	}
	
	//return the index of the TriSet in buffer
	private int getTriSet(int inodePageId, Transaction tx) {
		return get(inodePageId, null, tx);
	}
	
	
	@Override
	protected BufferObject createEmptyObject(int index, Transaction tx) {
		return new InsertableTriSet();
	}

	@Override
	protected void loadObject(int inodePageId, Object arg, int index, Transaction tx) {
		//PageDir inode = new PageDir(inodePageId, mmpfile, tx);
		PageDir inode = new PageDir(inodePageId, inodeFile, mmpfile, tx);
		IWriter writer = new WPageArray(inode, mmpfile, tx);
		((InsertableTriSet)buffer[index]).reset(inodePageId, inode, writer);
		
	}

	@Override
	protected void flushObject(int index, Transaction tx) {
		// do nothing, modification to set is write-through
		
	}
		
	public void dispose(Transaction tx) {
		indexLock.lock();
		try {
			for (int i = 0; i < buffer.length; ++i)
				if (buffer[i] != null) {
					synchronized (buffer[i]) {
						this.flushObject(i, tx);
						((InsertableTriSet)buffer[i]).dispose();
					}
				}
			
		} 
		finally {
			indexLock.unlock();
			mmpfile.close();
			if (inodeFile != mmpfile)
				inodeFile.close();
		}
	}

}
